import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { message, Tag } from 'antd';
import { customUpload } from '@/utils/fileUpload';
import BraftEditor from 'braft-editor';
import { ContentUtils } from 'braft-utils';
import { useMount } from 'react-use';
import { debounce } from 'lodash';
import Table from 'braft-extensions/dist/table';
import cn from 'classnames';
import styles from './index.less';

// 解码插入的变量
const decodeEditorValue = (str, insertParamList, extraParams) => {
    if (!str) {
        return '';
    }
    let tempStr = str;
    const arr = str.match(/\${[a-z]+}/gi);
    if (arr && arr.length > 0) {
        arr.forEach(item => {
            const field = item.replace('${', '').replace('}', '');
            if (extraParams.indexOf(field) > -1) {
                return;
            }
            const fieldName = insertParamList.find(c => c.field === field)?.value || '';
            tempStr = tempStr.replace(item, `<span data-foo="${field}" class="column-item">${fieldName}</span>`);
        });
    }
    return tempStr;
};

// 编码插入的变量
const encodeEditorValue = editorValue =>
    editorValue
        .replace(/<span data-foo="/g, '${')
        .replace(/" data-id="[0-9]+" class="column-item">[\u4e00-\u9fa5a-zA-Z0-9]+<\/span>/g, '}');

// 样式的短横线命名改成驼峰命名
const formatStringToCamelCase = str => {
    const splitted = str.split('-');
    if (splitted.length === 1) return splitted[0];
    return (
        splitted[0] +
        splitted
            .slice(1)
            .map(word => word[0].toUpperCase() + word.slice(1))
            .join('')
    );
};

// 把字符串的样式转换成对象格式
const getStyleObjectFromString = str => {
    if (!str) {
        return null;
    }
    const style = {};
    str.split(';').forEach(el => {
        const [property, value] = el.split(':');
        if (!property) return;

        const formattedProperty = formatStringToCamelCase(property.trim());
        style[formattedProperty] = value.trim();
    });

    return style;
};

// 自定义 hr
const HrComponent = ({ blockProps, block }) => {
    const removeBarBlock = () => {
        blockProps.editor.setValue(ContentUtils.removeBlock(blockProps.editorState, block));
    };

    return (
        <div className="hr-component">
            <button type="button" className="button-remove" onClick={removeBarBlock}>
                <i className="bfi-bin" />
            </button>
        </div>
    );
};

// 自定义block输入转换器，用于将符合规则的html内容转换成相应的block，通常与blockExportFn中定义的输出转换规则相对应
// eslint-disable-next-line consistent-return
const blockImportFn = (nodeName, node) => {
    if (node.classList.contains('custom-hr')) {
        return { type: 'custom-hr' };
    }
};

// 自定义block输出转换器，用于将不同的block转换成不同的html内容，通常与blockImportFn中定义的输入转换规则相对应
// eslint-disable-next-line consistent-return
const blockExportFn = (contentState, block) => {
    if (block.type === 'custom-hr') {
        return '<div class="custom-hr" style="height: 0; border-bottom: solid 1px #ddd; margin: 2em 0;"></div>';
    }
    // 因为插入的方法只有 ContentUtils.insertAtomicBlock 合适, 但是对于的 type 是 atomic, 所以做特殊处理
    if (block.type === 'atomic') {
        const ranges = block.entityRanges.length > 0 ? block.entityRanges[0] : -1;
        if (ranges !== -1) {
            const entity = contentState.getEntity(contentState.getBlockForKey(block.key).getEntityAt(0));
            if (entity.getType() === 'custom-hr') {
                return '<div class="custom-hr" style="height: 0; border-bottom: solid 1px #ddd; margin: 2em 0;"></div>';
            }
        }
    }
};
// eslint-disable-next-line consistent-return
const blockRendererFn = (block, { editor, editorState }) => {
    if (block.getType() === 'custom-hr') {
        return {
            component: HrComponent,
            editable: false, // 此处的editable并不代表组件内容实际可编辑，强烈建议设置为false
            props: { editor, editorState } // 此处传入的内容可以在组件中通过this.props.blockProps获取到
        };
    }
    if (block.getType() === 'atomic') {
        const entity = editorState.getCurrentContent().getEntity(block.getEntityAt(0));
        if (entity.getType() === 'custom-hr') {
            return {
                component: HrComponent,
                editable: false, // 此处的editable并不代表组件内容实际可编辑，强烈建议设置为false
                props: { editor, editorState } // 此处传入的内容可以在组件中通过this.props.blockProps获取到
            };
        }
    }
};

// 插入参数的参数列表
const InsertParamControlItem = ({ dataSource = [], onClick, ...restProps }) => {
    const liStyle = {
        backgroundColor: 'hsla(0,0%,100%,.1)',
        borderRadius: 2,
        margin: 5,
        color: '#fff',
        fontSize: 12,
        height: 30,
        lineHeight: '30px',
        textAlign: 'center',
        cursor: 'pointer',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap'
    };

    return (
        <ul style={{ width: 110, maxHeight: 250, overflow: 'auto' }} {...restProps}>
            {dataSource?.map(item => (
                <li
                    style={liStyle}
                    key={item.field}
                    onClick={() => {
                        if (onClick) {
                            onClick(item);
                        }
                    }}
                >
                    {item.value}
                </li>
            ))}
        </ul>
    );
};

let editorId = 100;

/**
 * 邮件模板编辑器
 */
const TemplateEditor = React.forwardRef(
    (
        { value, insertParamList = [], insertParamRender, initialValue, controls, readOnly, onChange, ...restProps },
        ref
    ) => {
        const idRef = React.useRef(0);
        const editorRef = React.useRef(
            BraftEditor.createEditorState(null, {
                editorId: `editor-with-entity-extension-${editorId}`,
                blockImportFn,
                blockExportFn
            })
        );
        const [editorValue, setEditorValue] = React.useState(editorRef.current);

        useMount(() => {
            const entityExtension = {
                // 指定扩展类型
                type: 'entity',
                // 指定该扩展对哪些编辑器生效，不指定includeEditors则对所有编辑器生效
                includeEditors: [`editor-with-entity-extension-${editorId}`],
                // 指定扩展的entity名称，推荐使用全部大写，内部也会将小写转换为大写
                name: 'COLUMN-ITEM',
                // 在编辑器工具栏中增加一个控制按钮，点击时会将所选文字转换为该entity
                // control: {
                //     text: '按键'
                // },
                // 指定entity的mutability属性，可选值为MUTABLE和IMMUTABLE，表明该entity是否可编辑，默认为MUTABLE
                mutability: 'IMMUTABLE',
                // 指定通过上面新增的按钮创建entity时的默认附加数据
                // data: {
                //     foo: ''
                // },
                // 指定entity在编辑器中的渲染组件
                component: props => {
                    const entity = props.contentState.getEntity(props.entityKey);
                    const { foo, id } = entity.getData();
                    const handleRemove = () => {
                        const childrenText = props.children?.[0]?.props?.text || '';
                        const html = editorRef.current
                            .toHTML()
                            .replace(
                                `<span data-foo="${foo}" data-id="${id}" class="column-item">${childrenText}</span>`,
                                ''
                            );
                        const obj = BraftEditor.createEditorState(html, {
                            editorId: `editor-with-entity-extension-${editorId}`,
                            blockImportFn,
                            blockExportFn
                        });
                        setEditorValue(obj);
                    };
                    return (
                        <Tag
                            key={id}
                            closable={!readOnly}
                            contentEditable={false}
                            style={{
                                paddingTop: 4,
                                paddingBottom: 4,
                                marginLeft: 4,
                                marginRight: 4,
                                marginBottom: 4,
                                textIndent: 0,
                                background: '#F0F7FF',
                                border: '1px solid #F0F7FF'
                            }}
                            onClose={() => handleRemove()}
                        >
                            {props.children}
                        </Tag>
                    );
                },
                // 指定html转换为editorState时，何种规则的内容将会转换成该entity
                // eslint-disable-next-line consistent-return
                importer: (nodeName, node) => {
                    // source属性表明输入来源，可能值为create、paste或undefined
                    if (nodeName.toLowerCase() === 'span' && node.classList && node.classList.contains('column-item')) {
                        // 此处可以返回true或者一个包含mutability和data属性的对象
                        let tempId = 0;
                        if (node.dataset.id) {
                            tempId = node.dataset.id;
                        } else {
                            idRef.current += 1;
                            tempId = idRef.current;
                        }
                        return {
                            mutability: 'IMMUTABLE',
                            data: {
                                id: tempId,
                                foo: node.dataset.foo
                            }
                        };
                    }
                },
                // 指定输出该entity在输出的html中的呈现方式
                exporter: (entityObject, originalText) => {
                    // 注意此处的entityObject并不是一个entity实例，而是一个包含type、mutability和data属性的对象
                    const { foo, id } = entityObject.data;
                    return (
                        <span data-foo={foo} data-id={id} className="column-item">
                            {originalText}
                        </span>
                    );
                }
            };

            // 保留 html 格式及 inline-style 样式
            const keepFormatElementEntity = {
                type: 'entity',
                includeEditors: [`editor-with-entity-extension-${editorId}`],
                name: 'KEEP-FORMAT-ELEMENT',
                mutability: 'MUTABLE',
                // 指定entity在编辑器中的渲染组件
                component: props => {
                    const entity = props.contentState.getEntity(props.entityKey);
                    const { nodeName, attributes } = entity.getData();
                    const { style, class: className, ...otherAttrs } = attributes;
                    const otherAttrsObj = {};
                    if (otherAttrs && otherAttrs.length) {
                        otherAttrs.each(item => {
                            otherAttrsObj[item.name] = item.value;
                        });
                    }
                    return React.createElement(
                        nodeName,
                        {
                            style: style?.value ? getStyleObjectFromString(style.value) : null,
                            className: className.value,
                            ...otherAttrsObj
                        },
                        props.children
                    );
                },
                // eslint-disable-next-line consistent-return
                importer: (nodeName, node) => {
                    if (node.classList && node.classList.contains('keepFormat')) {
                        return {
                            mutability: 'MUTABLE',
                            data: {
                                nodeName,
                                attributes: node.attributes
                            }
                        };
                    }
                },
                // 指定输出该entity在输出的html中的呈现方式
                exporter: (entityObject, originalText) => {
                    const { nodeName, attributes } = entityObject.data;
                    const attrStr = [...attributes].map(item => `${item.name}="${item.value}"`).join(' ');
                    return `<${nodeName} ${attrStr}>${originalText}</${nodeName}>`;
                }
            };

            // 加载扩展模块
            BraftEditor.use(
                Table({
                    defaultColumns: 4, // 默认列数
                    defaultRows: 7, // 默认行数
                    withDropdown: false, // 插入表格前是否弹出下拉菜单
                    columnResizable: true, // 是否允许拖动调整列宽，默认false
                    exportAttrString: 'border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse"', // 指定输出HTML时附加到table标签上的属性字符串
                    includeEditors: [`editor-with-entity-extension-${editorId}`]
                })
            );
            BraftEditor.use(entityExtension);
            BraftEditor.use(keepFormatElementEntity);
        });

        // 插入参数
        const handleInsert = useCallback(
            column => {
                let content = '';
                // 特殊参数的预处理
                if (insertParamRender?.[column.field]) {
                    content = insertParamRender[column.field].call(this, column.value);
                } else {
                    // 插入参数占位符
                    content = `<span class="column-item" data-foo="${column?.field}">${column?.value}</span>`;
                }
                const obj = ContentUtils.insertHTML(editorRef.current, content);
                setEditorValue(obj);
            },
            [insertParamRender]
        );

        // change事件, 并做长度校验
        const handleEditorChange = useCallback(editorState => {
            setEditorValue(editorState);
            const html = editorState.toHTML();
            // 内容校验
            if (html.length > 20000) {
                message.error('邮件内容过多');
            }
        }, []);

        // 不允许添加尺寸大于10M的文件
        const myValidateFn = useCallback(file => {
            if (file.size > 1024 * 1024 * 10) {
                message.error('图片不能大于10M！');
                return false;
            }
            return true;
        }, []);

        // 自定义上传的方法
        const myUploadFn = useCallback(param => {
            const onSuccess = (response, file) =>
                param.success({
                    url: response.data[0].url,
                    meta: {
                        id: file.name,
                        title: file.name,
                        alt: file.name
                    }
                });
            customUpload({ files: param, onSuccess });
        }, []);

        const onChangeDebounce = React.useRef(
            debounce(html => {
                if (onChange) onChange(encodeEditorValue(html));
            }, 500)
        );

        React.useEffect(() => {
            editorRef.current = editorValue;
            onChangeDebounce.current(editorValue.toHTML());
        }, [editorValue]);

        React.useEffect(() => {
            if (insertParamList) {
                setEditorValue(
                    BraftEditor.createEditorState(
                        decodeEditorValue(
                            initialValue,
                            insertParamList,
                            insertParamRender ? Object.keys(insertParamRender) : []
                        ),
                        {
                            editorId: `editor-with-entity-extension-${editorId}`,
                            blockImportFn,
                            blockExportFn
                        }
                    )
                );
            }
        }, [initialValue, insertParamList, insertParamRender]);

        React.useEffect(
            () => () => {
                idRef.current = 0;
                editorId += 1;
            },
            []
        );

        const editorProps = useMemo(
            () =>
                !readOnly
                    ? {
                          controls: [
                              'undo',
                              'redo',
                              'separator',
                              'font-size',
                              'line-height',
                              'letter-spacing',
                              'separator',
                              'text-color',
                              'bold',
                              'italic',
                              'underline',
                              'strike-through',
                              'separator',
                              'superscript',
                              'subscript',
                              'remove-styles',
                              'emoji',
                              'separator',
                              'text-indent',
                              'text-align',
                              'separator',
                              'headings',
                              'list-ul',
                              'list-ol',
                              'blockquote',
                              'code',
                              'separator',
                              'media',
                              'link',
                              // 'hr',
                              {
                                  key: 'insert-hr',
                                  title: '水平线',
                                  type: 'button',
                                  text: '水平线',
                                  onClick: () => {
                                      const obj = ContentUtils.insertAtomicBlock(editorRef.current, 'custom-hr', true);
                                      setEditorValue(obj);
                                  }
                              },
                              'separator',
                              'clear',
                              {
                                  key: 'insert-param',
                                  type: 'dropdown',
                                  title: '插入参数',
                                  className: 'insert-param',
                                  html: null,
                                  text: '插入参数',
                                  showArrow: true,
                                  arrowActive: false,
                                  autoHide: true,
                                  component: (
                                      <InsertParamControlItem dataSource={insertParamList} onClick={handleInsert} />
                                  )
                              }
                          ].concat(controls ?? []),
                          media: {
                              uploadFn: myUploadFn,
                              validateFn: myValidateFn,
                              accepts: {
                                  image: 'image/png,image/jpeg,image/gif'
                              }
                          },
                          excludeControls: ['fullscreen'],
                          imageControls: [
                              {
                                  onClick: block => {
                                      console.log(block);
                                  }
                              },
                              'align-left',
                              'align-center',
                              'align-right',
                              'size',
                              'remove'
                          ],
                          contentStyle: { height: 600 }
                      }
                    : {
                          controls: [],
                          readOnly,
                          contentStyle: { height: 'auto' }
                      },
            [controls, handleInsert, insertParamList, myUploadFn, myValidateFn, readOnly]
        );

        return (
            <div className={cn(styles.templateEditor, readOnly ? styles.readonly : null)}>
                <BraftEditor
                    ref={ref}
                    value={editorValue}
                    onChange={handleEditorChange}
                    placeholder="请输入内容"
                    contentStyle={{ height: 600 }}
                    {...editorProps}
                    {...restProps}
                    converts={{ blockImportFn, blockExportFn }}
                    blockRendererFn={blockRendererFn}
                    id={`editor-with-entity-extension-${editorId}`}
                />
            </div>
        );
    }
);

TemplateEditor.propTypes = {
    insertParamList: PropTypes.array,
    insertParamRender: PropTypes.object,
    initialValue: PropTypes.string,
    controls: PropTypes.array,
    readOnly: PropTypes.bool
};
TemplateEditor.defaultProps = {
    insertParamList: [],
    insertParamRender: null,
    initialValue: '',
    controls: null,
    readOnly: false
};
TemplateEditor.displayName = 'TemplateEditor';

export default TemplateEditor;
